﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Data;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Events;
using Microsoft.Practices.Prism.Regions;
using Tanmia.Basis.Core;
using Tanmia.Basis.Domain;
using Tanmia.Basis.Prism;
using Tanmia.Basis.Properties;
using Tanmia.Basis.Security;
using Tanmia.Basis.ServiceInterface;
using Tanmia.Basis.ViewInterface;

namespace Tanmia.Basis.ViewModel
{
    /// <summary>
    /// Represent the base view model class, for each Browser screen.
    /// </summary>
    /// <typeparam name="TCore">Any business entity class which is derived from the BaseBusinessObject.</typeparam>
    public class BrowserViewModel<TCore> : BaseViewModel, IBusinessEntityNavigator<TCore>
        where TCore : BaseBusinessObject
    {
        private readonly BackgroundWorker refreshBackgroundWorker;

        #region Protected Properties

        /// <summary>
        /// Gets domain object of the Entity object.
        /// </summary>
        protected BusinessDomain<TCore> EntityObj { get; set; }

        /// <summary>
        /// Gets a reference to the user messaging service.
        /// </summary>
        protected IMessageService MessageService { get; private set; }

        /// <summary>
        /// Gets a reference to the RegionManager service.
        /// </summary>
        protected IRegionManager RegionManager { get; private set; }

        /// <summary>
        /// Gets a reference to the EventAggregator service.
        /// </summary>
        protected IEventAggregator EventAggregator { get; private set; }

        /// <summary>
        /// Gets string message confirmation to be shown to the user in the Delete Confirmation message box.
        /// </summary>
        protected virtual string DeleteConfirmationMessage
        {
            get { return "Are you sure to delete? "; }
        }

        #endregion Protected Properties

        #region Public Properties

        /// <summary>
        /// Gets human readable name of the business entity.
        /// </summary>
        public string EntityName { get; protected set; }

        /// <summary>
        /// Gets a command which represent refreshing of the BrowserView.
        /// </summary>
        public ICommand RefreshCommand { get; protected set; }

        /// <summary>
        /// Gets the command to invoke when (Delete) button is pressed.
        /// </summary>
        public ICommand DeleteCommand { get; protected set; }

        /// <summary>
        /// Gets or sets a CollectionView of all entities which will be showed in the browser view.
        /// </summary>
        public ICollectionView Entities { get; set; }

        /// <summary>
        /// Gets all entities which are selected in the UI.
        /// </summary>
        /// <remarks>
        /// This property have to be overriden in derived class to specifiy how the selected entities should be returned.
        /// </remarks>
        public virtual IEnumerable<TCore> SelectedEntities
        {
            get { return null; }
        }

        #endregion Public Properties

        public BrowserViewModel(IShellView shellView, IRegionManager regionManager, IEventAggregator eventAggregator, IMessageService messageService, BusinessDomain<TCore> domainObject)
            : base(shellView)
        {
            this.RegionManager = regionManager;
            this.MessageService = messageService;
            this.EntityObj = domainObject;
            this.EventAggregator = eventAggregator;

            refreshBackgroundWorker = new BackgroundWorker();
            refreshBackgroundWorker.DoWork += new DoWorkEventHandler(refreshBackgroundWorker_DoWork);
            refreshBackgroundWorker.RunWorkerCompleted += (sender, args) => CompleteRefresh();

            // x. Command creation
            RefreshCommand = new DelegateCommand(OnRefresh);
            DeleteCommand = new DelegateCommand(OnDelete, OnCanDelete);
            CloseCommand = new DelegateCommand(OnClose);

            // x1. Wire events
            EventAggregator.GetEvent<BrowserRefreshEvent<TCore>>().Subscribe(EditorRefreshHanler);
        }

        #region Core Logic

        /// <summary>
        /// Close the view which is associated with this view model and dispose the current view model.
        /// </summary>
        protected virtual void CoreClose()
        {
            object view = RegionManager.GetView(this);
            IRegion windowRegion = RegionManager.Regions[Constants.RegionMain];
            windowRegion.Remove(view);

            this.Dispose();
        }

        /// <summary>
        /// Execute main flow of Refresh the browser grid.
        /// </summary>
        protected virtual void CoreRefresh()
        {
            EntityObj.CloseSession();

            // Unwire the event to avoid the leak.
            if (Entities != null)
                Entities.CurrentChanged -= CurrentChangedHandler;

            Entities = MapEntities(EntityObj.Gets());// CollectionViewSource.GetDefaultView(EntityObj.Gets());

            // Wire event to 
            Entities.CurrentChanged += new EventHandler(CurrentChangedHandler);

            RaisePropertyChanged(() => Entities);
        }

        protected virtual ICollectionView MapEntities(IList<TCore> entities)
        {
            return CollectionViewSource.GetDefaultView(entities);
        }

        /// <summary>
        /// Execute main flow of Delete entity (passed parameter).
        /// </summary>
        /// <returns>True, if the entity has been deleted successfully. False, otherwise.</returns>
        /// <remarks>
        /// 1- Delete the entity which is passed as parameter.
        /// 3- Return the result
        /// </remarks>
        protected virtual bool CoreDelete(TCore entity)
        {
            bool retVal = false;

            try
            {
                EntityObj.Delete(entity);
                retVal = true;
            }
            catch (AccessDeniedSecurityException secEx)
            {
                MessageService.ShowSecurityMessage(secEx, true);
            }

            return retVal;
        }
        #endregion

        #region Main Business Logic

        /// <summary>
        /// Determine if Delete button can be pressed or not.
        /// </summary>
        /// <returns></returns>
        protected virtual bool OnCanDelete()
        {
            if (CurrentEntity == null)
                return false;

            return Entities.CurrentItem != null;
        }

        /// <summary>
        /// Delete current Entity from the database.
        /// this method should be overriden in the derived class, to specify the User confirmation method about the delete.
        /// </summary>
        protected virtual void OnDelete()
        {
            MessageServiceResultEnum result = MessageService.RequestUserConfirmation(DeleteConfirmationMessage);
            bool deletionResult = true;
            int entityCount = 0;

            if (result == MessageServiceResultEnum.Yes)
            {
                entityCount = SelectedEntities.Count();

                foreach (TCore entity in SelectedEntities)
                    deletionResult &= CoreDelete(entity);

                OnRefresh();

                UpdateStatus(deletionResult, Resources.DeleteSucceed, Resources.DeleteUnsucceed, entityCount, EntityName);
            }
        }

        /// <summary>
        /// Close screen command handler.
        /// </summary>
        /// <remarks>
        /// 1- CoreClose
        /// </remarks>
        protected virtual void OnClose()
        {
            CoreClose();

            UpdateStatus(Resources.CloseBrowser, EntityName);
        }

        /// <summary>
        /// Reftesh the grid browser command handler
        /// </summary>
        /// <remarks>
        /// 1- UpdateStatus
        /// 2- Run background thread to refresh the grid.
        /// </remarks>
        protected virtual void OnRefresh()
        {
            UpdateStatus(Resources.RefreshBrowser, EntityName);
            refreshBackgroundWorker.RunWorkerAsync();
        }

        #endregion

        #region Event Handler
        /// <summary>
        /// The background thread method which is going to re-fill the Entites.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void refreshBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            CoreRefresh();
        }

        /// <summary>
        /// Occurs when the Entities has been filled with new List from the database.
        /// </summary>
        protected virtual void CompleteRefresh()
        {
            UpdateStatus(Resources.Ready);
        }

        /// <summary>
        /// Occurs when the EditorViewModel calls RefreshBrowser.
        /// </summary>
        /// <param name="entity">Represent the Current entity on the Editor screen.</param>
        private void EditorRefreshHanler(TCore entity)
        {
            OnRefresh();
        }

        protected virtual void CurrentChangedHandler(object sender, EventArgs e)
        {
            ((DelegateCommand)DeleteCommand).RaiseCanExecuteChanged();
        }

        #endregion

        #region Overriden Methods

        public override void OnNavigatedTo(NavigationContext navigationContext)
        {
            base.OnNavigatedTo(navigationContext);

            UpdateStatus(Resources.OpenBrowserScreen, EntityName);
        }

        #endregion Overriden Methods

        #region IBusinessEntityNavigator Members

        /// <summary>
        /// Gets the selected entity on the Browser Window.
        /// </summary>
        public TCore CurrentEntity
        {
            get
            {
                if (Entities != null)
                    return Entities.CurrentItem as TCore;

                return null;
            }
        }

        /// <summary>
        /// Gets a value determine if the CurrentEntity is standing at the first Record.
        /// </summary>
        public bool IsAtFirst
        {
            get
            {
                if (Entities == null)
                    return true;
                else
                    return Entities.CurrentPosition == 0;
            }
        }

        /// <summary>
        /// Gets a value determine if the CurrentEntity is standing at the last Record.
        /// </summary>
        public bool IsAtLast
        {
            get
            {
                IList<TCore> collection = null;
                if (Entities != null)
                    collection = Entities.SourceCollection as IList<TCore>;

                if (collection == null)
                    return true;
                else
                    return collection.Count - 1 == Entities.CurrentPosition;
            }
        }

        /// <summary>
        /// Move CurrentEntity back to the previous record.
        /// </summary>
        public void MoveToPrevious()
        {
            Entities.MoveCurrentToPrevious();
        }

        /// <summary>
        /// Move CurrentEntity forward to the next record.
        /// </summary>
        public void MoveToNext()
        {
            Entities.MoveCurrentToNext();
        }

        /// <summary>
        /// Move CurrentEntity top to the first record.
        /// </summary>
        public void MoveToFirst()
        {
            Entities.MoveCurrentToFirst();
        }

        /// <summary>
        /// Move CurrentEntity down to the last record.
        /// </summary>
        public void MoveToLast()
        {
            Entities.MoveCurrentToLast();
        }

        #endregion IBusinessEntityNavigator Members
    }
}
